#include "ptwrap.hpp"

namespace pw {

// [[ex05ae]]
// DEBUG descr A convenience method to use the event as an interruptible timeout.
class event
{
	// DEBUG { comes from ex03ag
public:
	event(bool signaled = false) :
		signaled_(signaled),
		seq_(0),
		seqpulse_(0)
	{ }

	int wait()
	{
		pw::lockmutex lm(cond_);
		if (signaled_)
			return 0;
		unsigned s = ++seq_;
		do {
			cond_.wait();
			if (seqpulse_ - s >= 0)
				return 0;
		} while (!signaled_);
		return 0;
	}
	int trywait()
	{
		pw::lockmutex lm(cond_);
		// doesn't need the sequence because doesn't
		// care about pulsing
		if (!signaled_)
			return ETIMEDOUT;
		return 0;
	}
	int timedwait(const struct timespec &abstime)
	{
		pw::lockmutex lm(cond_);
		if (signaled_)
			return 0;
		unsigned s = ++seq_;
		do {
			if (cond_.timedwait(abstime) == ETIMEDOUT)
				return ETIMEDOUT;
			if (seqpulse_ - s >= 0)
				return 0;
		} while (!signaled_);
		return 0;
	}
	int signal()
	{
		pw::lockmutex lm(cond_);
		signaled_ = true;
		cond_.broadcast();
		return 0;
	}
	int reset()
	{
		pw::lockmutex lm(cond_);
		signaled_ = false;
		return 0;
	}
	int pulse()
	{
		pw::lockmutex lm(cond_);
		signaled_ = false;
		seqpulse_ = seq_;
		cond_.broadcast();
		return 0;
	}

protected:
	pw::pmcond cond_; // contains both condition variable and a mutex
	bool signaled_; // event is in the signaled state
	unsigned seq_; // every wait increases the sequence
	unsigned seqpulse_; // pulse frees all the waits up to this sequence
	// DEBUG }
public:
	// ...
	int msecwait(int milliseconds)
	{
		struct timespec endtime;
		// the current time
		clock_gettime(CLOCK_REALTIME, &endtime); 
		endtime.tv_sec += (milliseconds / 1000);
		endtime.tv_nsec += (milliseconds % 1000) 
			* (1000*1000);
		if (endtime.tv_nsec > 1000*1000*1000) {
			endtime.tv_nsec -= 1000*1000*1000;
			endtime.tv_sec += 1;
		}
		return timedwait(endtime);
	}
	// ...
};
// END

};
